#ifndef GEOGRAPHICUTIL_H
#define GEOGRAPHICUTIL_H

/**
 * @file geographicutil.h
 * @author your name (you@domain.com)
 * @brief 地理计算
 * @version 0.1
 * @date 2023-11-21
 *
 * @copyright Copyright (c) 2023
 *
 */

// #include "CommonUtils_global.h"
#include <QPair>
#include <QPointF>
// #include "GeographicLib/Geodesic.hpp"
// #include "GeographicLib/Rhumb.hpp"
#include "doublepair.h"

namespace ns_yht
{

    // typedef QPair<double, double> DoublePair;
    // typedef QVector<DoublePair> DoublePairs;

    class /*COMMONUTILS_EXPORT*/ GeographicUtil
    {
    public:
        GeographicUtil();

        /**
         * @brief Get the Geo Area object 平方米
         *
         * @param coordinates
         * @return double 平方米
         */
        static double getGeoArea(const DoublePairs &coordinates, bool isGeodesic);
        /**
         * @brief Get the Geo Distance object 米
         *
         * @param coordinates
         * @return double  米
         */
        static double getGeoDistance(const DoublePairs &coordinates, bool isGeodesic);

        /**
         * @brief 点到线段距离，垂足可以在线段上或者不在线段上
         *
         * @param point
         * @param segPoint0
         * @param segPoint1
         * @return double
         */
        static double getGeoDistance(const DoublePair &point, const DoublePair &segPoint0, const DoublePair &segPoint1);

        /**
         * @brief 一个点到一个形状的距离
         *
         * @param point
         * @param shape
         * @return double
         */
        static double distanceToPolygon(const DoublePair &point, const DoublePairs &shape);

        static double distanceToPolyline(const DoublePair &point, const DoublePairs &polyline);

        static double distanceToPie(const DoublePair &point, const DoublePairs &pie);

        /**
         * @brief Get the Geo Distance object 米
         *
         * @param coor1
         * @param coor2
         * @param isGeodesic 是否是测地线(大圆航线)
         * @return double  米
         */
        static double getGeoDistance(const DoublePair &coor1, const DoublePair &coor2, bool isGeodesic = true);

        /**
         * @brief 计算两个经纬度坐标构成的方位角,正北为0，角度，(-180，180]
         *
         * @param coor1
         * @param coor2
         * @return double 角度，(-180，180]
         */
        static double getGeoAngle(const DoublePair &coor1, const DoublePair &coor2);
        static double getGeoAngle_Radians(const DoublePair &coor1, const DoublePair &coor2);

        /**
         * @brief 计算恒向线方位
         *
         * @param lat1
         * @param lon1
         * @param lat2
         * @param lon2
         * @return double 角度
         */
        static double calculateHeading(double lat1, double lon1, double lat2, double lon2);
        static double calculateHeading(const DoublePair &coor1, const DoublePair &coor2);

        /**
         * @brief 计算恒向线距离
         *
         * @return double 米
         */
        static double calculateRhumbDistance(double lat1, double lon1, double lat2, double lon2);

        /**
         * @brief 计算恒向线距离
         *
         * @return double 米
         */
        static double calculateRhumbDistance(const DoublePair &coor1, const DoublePair &coor2);

        /**
         * @brief 计算测地线(大圆航线)距离
         *
         * @return double 海里
         */
        static double calculateGeodesicDistance(double lat1, double lon1, double lat2, double lon2);

        /**
         * @brief 计算测地线(大圆航线)距离
         *
         * @return double 海里
         */
        static double calculateGeodesicDistance(const DoublePair &coor1, const DoublePair &coor2);

        /**
         * @brief gtLat 对某个（任意）经度上从纬度lat1开始向上距离位distance米的点，返回对应的纬度
         * @param lat1
         * @param distance
         * @param up 向北（true）还是向南（false）
         * @return
         */
        static double getLat(double lat1, double distance, bool up = true);

        /**
         * @brief getLon 和[lat1,lon1]保持相同纬度且相距distance米的点的经度
         * @param lat1
         * @param lon1
         * @param distance 距离，单位：米
         * @param up 向东（true）还是向西（false）
         * @return
         */
        static double getLon(double lat1, double lon1, double distance, bool up);

        /**
         * @brief 从[lat1,lon1]出发向angle方向延伸distance米距离后的位置
         * @param lat1
         * @param lon1
         * @param angle 方位，角度，正北为0，顺时针方向，0-360
         * @param distance 距离，单位：米
         * @param lat2
         * @param lon2
         */
        static void getCoordinate(double lat1, double lon1, double angle, double distance, double &lat2, double &lon2);

        /**
         * @brief coor1为起点，在coor1到coor2的线上取distance米距离，返回newCoor
         * @param coor1
         * @param coor2
         * @param distance 米
         * @param newCoor
         */
        static void getCoordinate(const DoublePair &coor1, const DoublePair &coor2, double distance, DoublePair &newCoor);

        /**
         * @brief SimplifyLine 精简一组连续点（线条），距离很近的点合成一个点，坐标为经纬度
         * @param spoints   输入点数组（输入线条）
         * @param dpoints   输出点数组（输出线条）
         * @param dis       误差阈值
         */
        static void simplifyLine(const DoublePairs &spoints, DoublePairs &dpoints, double dis = 0.1);

        /**
         * @brief PointInGeometry   点是否在多边形内部，经纬度
         * @param lat
         * @param lon
         * @param coordinates
         * @return true-内部   false-外部
         */
        static bool pointInGeometry(double lat, double lon, const DoublePairs &coordinates);
        static bool pointInGeometry(double lat, double lon);

        static void setStaticPolygon(const DoublePairs &coordinates);

        /**
         * @brief PointInGeometry 点是否在多边形内部，经纬度
         * @param lat
         * @param lon
         * @param coordinates POLYGON((lon0 lat0,lon1 lat1,lon2 lat2,......))
         * @return true-内部   false-外部
         */
        static bool pointInGeometry(double lat, double lon, const QString &coordinates);

        /**
         * @brief 两个形状是否相交
         *
         * @param coordinates1
         * @param coordinates2
         * @return true 相交
         * @return false 不相交
         */
        static bool intersects_lineToPoly(const DoublePairs &coordinates1, const DoublePairs &coordinates2);
        static bool intersects_lineToPoly(const DoublePairs &coordinates);
        static bool intersects_polyToPoly(const DoublePairs &coordinates1, const DoublePairs &coordinates2);
        static bool intersects_polyToPoly(const DoublePairs &coordinates);
        static void intersects_line(const DoublePairs &coordinates1, const DoublePairs &coordinates2, DoublePair &intersect);
        /**
         * @brief 两个区域的交集
         *
         * @param coordinates1
         * @param coordinates2
         * @return QVector<DoublePairs>
         */
        static bool intersection_polyToPoly(const QVector<DoublePairs> &geometry1, const QVector<DoublePairs> &geometry2, QVector<DoublePairs> &result);
        template <class T>
        static void convertPolygonToPolygon(const QVector<DoublePairs> &geometry1, T &result);

        /**
         * @brief 以一个点为中心的圆
         *
         * @param lat
         * @param lon
         * @param distance  半径，米
         * @param circle    返回的圆坐标数组
         * @param circlePointCount  需要返回的圆的点数，点数越多越平滑
         */
        static void getPointCircle(double lat, double lon, double distance, DoublePairs &circle, int circlePointCount = 36);

        /**
         * @brief 获取经纬度构成的线段的平行线
         *
         * @param point1
         * @param point2
         * @param distance
         * @param isLeft 是否左边
         * @param parallel
         */
        static void getParallelLine(const DoublePairs &line, double distance, bool isLeft, DoublePairs &parallel);

        /**
         * @brief 获取经纬度构成的线段的左右平行线
         *
         * @param line
         * @param loffset
         * @param roffset
         * @param lParallel
         * @param rParallel
         */
        static void getParallelLines(const DoublePairs &line, double loffset, double roffset, DoublePairs &lParallel, DoublePairs &rParallel);
        /**
         * Calculates an arc based on the given parameters.
         *
         * @param lat the latitude of the center point of the arc
         * @param lon the longitude of the center point of the arc
         * @param distance the distance from the center point of the arc to the outer point,米
         * @param startAngle the starting angle of the arc in degrees.[0-360)  north is 0
         * @param sweepAngle sweeped angle in degrees,[-360-360)
         * @param arc the output container to store the arc points
         *
         * @throws None
         */
        static void getArc(double lat, double lon, double distance, double startAngle, double sweepAngle, DoublePairs &arc);

        static void getCircleCoordinates(const DoublePair &center, const DoublePair &radiusPoint, DoublePairs &coordinates);
        static void getCircleCoordinates(const DoublePair &center, double radius, DoublePairs &coordinates);
        // void getEllipseCoordinates(double slat, double slon, double elat, double elon, QYHHMap::Coordinates &coordinates, double alpha = 0) const;
        static void getArcCoordinates(const DoublePair &center, const DoublePair &startPoint, double sweepAngle, DoublePairs &coordinates);

        /**
         * @brief 获取多个点构成的大圆航线（测地线）
         *
         * @param lineCoors 原始折线坐标
         * @param greatCircleCoors 大圆航线坐标
         * @param centerCoors 每一段折线段对应的大圆航线的中间点
         */
        static void getGreateCircleCoors(const DoublePairs &lineCoors, DoublePairs &greatCircleCoors, DoublePairs &centerCoors);

        /**
         * @brief 获取两个点构成的大圆航线（测地线）,不包含起点终点
         *
         * @param lat1
         * @param lon1
         * @param lat2
         * @param lon2
         * @param greatCircleCoors
         */
        static void getGreateCircleCoors(double lat1, double lon1, double lat2, double lon2, DoublePairs &greatCircleCoors);

        static boostgm::model::polygon<GeoPoint> static_poly;

        static const double MetersPerNM;
        static const double MY_PI;
        // static const double EARTH_RADIUS;
        // static const double EARTH_RADIUS2;

        // static const GeographicLib::Geodesic geod;
        // static const GeographicLib::Rhumb rhumb;
    };

} //  namespace ns_yht

#endif // GEOGRAPHICUTIL_H
